package edu.indiana.d2i.vmm;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.indiana.d2i.vmm.job.SigiriHTRCJobExeContainer;
import edu.indiana.d2i.vmm.vm.BasicHTRCVMController;
import edu.indiana.extreme.sigiri.JobInfoBean;
import edu.indiana.extreme.sigiri.util.ConnectionManager;
import edu.indiana.extreme.sigiri.util.Constants;
import edu.indiana.extreme.sigiri.util.DBUtil;
import edu.indiana.extreme.sigiri.util.SigiriException;

public abstract class SigiriHTRCJobManager implements Runnable {
	protected BasicHTRCVMController vmController = null;
	protected ConnectionManager connectionManager = null; 
	protected JobInfoBean jobInfoBean = null;
	protected String jobManagerId;
	
	private String sigiritHTRCJobID = null;
	private int waitTime;
	
	@SuppressWarnings("serial")
	private final Set<String> runningState = new HashSet<String>() {{
		add(Constants.JobStatus.NOT_AVAILABLE);
		add(Constants.JobStatus.STATE_IDLE); add(Constants.JobStatus.STATE_JOB_HELD);
	    add(Constants.JobStatus.STATE_JOB_STARTED); add(Constants.JobStatus.SUBMITTED_TO_JOB_MANAGER);
	}};
	
	private static final Log logger = LogFactory.getLog(SigiriHTRCJobManager.class);
	
	protected abstract JobInfoBean submitJob(JobInfoBean jobInfoBean);
	protected abstract void clearupJob();
	
	protected String getJobID() {
		if (sigiritHTRCJobID != null) return sigiritHTRCJobID;
		Calendar currentDate = Calendar.getInstance();
		SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd_HHmm");
		String dateNow = formatter.format(currentDate.getTime());
		sigiritHTRCJobID = "sigirihtrcjob_" + dateNow;
		return sigiritHTRCJobID;
	}
	
	public SigiriHTRCJobManager(ConnectionManager connectionManager, BasicHTRCVMController vmController, JobInfoBean jobInfoBean) {
		this.connectionManager = connectionManager;
		this.vmController = vmController;
		this.jobInfoBean = jobInfoBean;
	}
    
    public void setJobManagerId(String jobManagerId) {
        this.jobManagerId = jobManagerId;
    }
	
	/**
	 * @return true to indicate it needs to be updated again, false to indicate no need to update again
	 */
	public boolean updateJobStatus() {
		// avoid duplicated update!!!
		try {			
			if (jobInfoBean.getStatus().equals(Constants.JobStatus.JOB_SUBMISSION_FAILED))
				return false;
			
			String statusString = vmController.getJobStatusString();
			logger.info("statusString " + statusString);
			jobInfoBean.setStatus(statusString);
			DBUtil.updateJobTable(connectionManager, jobInfoBean);
			
			if (!runningState.contains(statusString)) {
				clearupJob();
				return false;
			}
			return true;
		} catch (Exception e) {
			logger.equals(e);
			return true;
		}
	}

	public String getJobStatus() {
		try {
			return vmController.getJobStatusString();
		} catch (Exception e) {
			logger.error(e);
			return null;
		}
	}
	
	public void run() {
		if (connectionManager == null || vmController == null || jobInfoBean == null) {
			logger.fatal("Database connector or JobInfoBean or VMController is null, can't proceed");
			return;
		}
		
		try {
			long beforeJobSubmissionTime = 0, afterJobSubmissionTime = 0;
	        long sigiriSubmittedTime = jobInfoBean.getJobSubmittedTime();
	        long daemonJobSubmissionStartTime = new Date().getTime();
	        String internalJobId = jobInfoBean.getInternalId();
	        logger.info("[" + jobManagerId + "]" + "  Working on Job " + internalJobId);
	        
	        // submit the job
            beforeJobSubmissionTime = new Date().getTime();
            JobInfoBean returnedjobInfoBean = submitJob(jobInfoBean);
            afterJobSubmissionTime = new Date().getTime();

            returnedjobInfoBean.setInternalId(internalJobId);
            returnedjobInfoBean.setJobSubmittedTime(new Date().getTime());

            String jobId = returnedjobInfoBean.getJobId();
            if (jobId != null && !"NoJobId".equals(jobId)) {
                // we have a job id and that means the job was submitted successfully.
                // let's get the job status from the load leveler
                logger.info("Job Submitted Successfully. Job Id => " + returnedjobInfoBean.getJobId());

                String status = getJobStatus(); 
                if (status == null) status = Constants.JobStatus.SUBMITTED_TO_JOB_MANAGER;
                returnedjobInfoBean.setStatus(status);
                jobInfoBean.setStatus(status);
                jobInfoBean.setJobId(jobId);
                DBUtil.updateJobTable(connectionManager, returnedjobInfoBean);
            } else {
                // job submission failed
                logger.info("Job submission failed. Reason => " + returnedjobInfoBean.getErrorDescription());
                returnedjobInfoBean.setStatus(Constants.JobStatus.JOB_SUBMISSION_FAILED);
                if (returnedjobInfoBean.getErrorDescription() == null) {
                    returnedjobInfoBean.setErrorDescription("Job rejected by the job manager. Check you job script");
                }
                jobInfoBean.setStatus(Constants.JobStatus.JOB_SUBMISSION_FAILED);
                DBUtil.updateJobTable(connectionManager, returnedjobInfoBean);
                vmController.releaseVMs();
            }
            
            long jobSubmisionTime = afterJobSubmissionTime - beforeJobSubmissionTime;
            logger.info("[Id=" + jobInfoBean.getInternalId() + "]" +
                    "[SigiriQueuedTime=" + (daemonJobSubmissionStartTime - sigiriSubmittedTime) + "]" +
                    "[JobProcessingOverhead=" + (daemonJobSubmissionStartTime - new Date().getTime() - jobSubmisionTime) + "]" +
                    "[JobSubmissionTimeToLL=" + jobSubmisionTime + "]");
		} catch (SigiriException e) {
			// job submission error is handled in concrete sub class and VMs are released there too
            // here is jus the error for Sigiri 
			logger.fatal(e.getMessage());
			vmController.releaseVMs();
        }
	}
}
